﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Ccr.Core;
using System.Threading;

namespace XcoAppSpaces.Core
{
	/// <summary>
	/// Helpers for installing different kinds of CCR receivers on ports.
	/// </summary>
	class PortReceiveHelpers
	{
		#region Concurrent Receive
		public static void InstallConcurrentReceiver<TMessage>(Port<TMessage> port, DispatcherQueue dpq, Handler<TMessage> handler)
		{
#if !PocketPC
			InstallConcurrentReceiver(port, dpq, handler, SyncContext.NoSyncContext);
		}

		public static void InstallConcurrentReceiver<TMessage>(Port<TMessage> port, DispatcherQueue dpq, Handler<TMessage> handler, SyncContext syncContext)
		{
			Handler<TMessage> safeHandler = handler;

			if (syncContext == SyncContext.UseCurrentSyncContext)
				safeHandler = CreateHandlerForSyncContext(handler);
#else
			Handler<TMessage> safeHandler = handler;
#endif

			Arbiter.Activate(
				dpq,
				Arbiter.Receive(
					true,
					port,
					safeHandler
					)
				);
		}
		#endregion

		#region Sequential Receive
		public static void InstallSequentialReceiver<TMessage>(Port<TMessage> port, DispatcherQueue dpq, Handler<TMessage> handler)
		{
#if !PocketPC
			InstallSequentialReceiver(port, dpq, handler, SyncContext.NoSyncContext);
		}

		public static void InstallSequentialReceiver<TMessage>(Port<TMessage> port, DispatcherQueue dpq, Handler<TMessage> handler, SyncContext syncContext)
		{
			Handler<TMessage> safeHandler = handler;

			if (syncContext == SyncContext.UseCurrentSyncContext)
				safeHandler = CreateHandlerForSyncContext(handler);
#else
			Handler<TMessage> safeHandler = handler;
#endif

			Arbiter.Activate(
				dpq,
				port.Receive(e =>
				{
					try
					{
						safeHandler(e);
					}
					finally
					{
						InstallSequentialReceiver(port, dpq, safeHandler);
					}
				}));
		}
		#endregion

		#region Iterator Receive
		public static void InstallIteratortReceiver<TMessage>(Port<TMessage> port, DispatcherQueue dpq, IteratorHandler<TMessage> handler)
		{
			Arbiter.Activate(
				dpq,
				Arbiter.ReceiveWithIterator(
					true,
					port,
					handler
					)
				);
		}
		#endregion

		#region Joined Receive
		public static void InstallJoinReceiver<T1, T2>(Port<T1> port1, Port<T2> port2, DispatcherQueue dpq, Handler<T1, T2> handler)
		{
#if !PocketPC
			InstallJoinReceiver(port1, port2, dpq, handler, SyncContext.NoSyncContext);
		}

		public static void InstallJoinReceiver<T1, T2>(Port<T1> port1, Port<T2> port2, DispatcherQueue dpq, Handler<T1, T2> handler, SyncContext syncContext)
		{
			Handler<T1, T2> safeHandler = handler;

			if (syncContext == SyncContext.UseCurrentSyncContext)
				safeHandler = CreateHandlerForSyncContext(handler);
#else
			Handler<T1, T2> safeHandler = handler;
#endif

			Arbiter.Activate(
				dpq,
				Arbiter.JoinedReceive(
					true,
					port1,
					port2,
					safeHandler
					)
				);
		}
		#endregion

		#region Choice Receive
		public static void InstallChoiceReceiver<T1, T2>(Port<T1> port1, Port<T2> port2, DispatcherQueue dpq, Handler<T1> handler1, Handler<T2> handler2)
		{
#if !PocketPC
			InstallChoiceReceiver(port1, port2, dpq, handler1, handler2, SyncContext.NoSyncContext);
		}

		public static void InstallChoiceReceiver<T1, T2>(Port<T1> port1, Port<T2> port2, DispatcherQueue dpq, Handler<T1> handler1, Handler<T2> handler2, SyncContext syncContext)
		{
			Handler<T1> safeHandler1 = handler1;
			Handler<T2> safeHandler2 = handler2;

			if (syncContext == SyncContext.UseCurrentSyncContext)
			{
				safeHandler1 = CreateHandlerForSyncContext(handler1);
				safeHandler2 = CreateHandlerForSyncContext(handler2);
			}
#else
			Handler<T1> safeHandler1 = handler1;
			Handler<T2> safeHandler2 = handler2;
#endif

			Arbiter.Activate(
				dpq,
				Arbiter.Choice(
					Arbiter.Receive(false, port1, safeHandler1),
					Arbiter.Receive(false, port2, safeHandler2)
					)
				);
		}
		#endregion

		#region Sync Handler Creation
#if !PocketPC
		public static Handler<T> CreateHandlerForSyncContext<T>(Handler<T> handler)
		{
			var currentContext = SynchronizationContext.Current;
			if (currentContext == null)
				return handler;

			Handler<T> safeHandler = x =>
			{
				IEnumerable<ICausality> activeCausalities = CausalityHelpers.ListOfActiveCausalities;
				currentContext.Post(
					delegate
					{
						CausalityHelpers.InstallCausalities(activeCausalities);
						handler(x);
						CausalityHelpers.ClearCausalities();
					},
					null);
			};
			return safeHandler;
		}

		public static Handler<T1, T2> CreateHandlerForSyncContext<T1, T2>(Handler<T1, T2> handler)
		{
			var currentContext = SynchronizationContext.Current;
			if (currentContext == null)
				return handler;

			Handler<T1, T2> safeHandler = (x, y) =>
			{
				IEnumerable<ICausality> activeCausalities = CausalityHelpers.ListOfActiveCausalities;
				currentContext.Post(
					delegate
					{
						CausalityHelpers.InstallCausalities(activeCausalities);
						handler(x, y);
						CausalityHelpers.ClearCausalities();
					},
					null);
			};
			return safeHandler;
		}
#endif
		#endregion
	}
}
